function Map() {
	
	this.SIZE = 64; // Size of map in tiles
	this.GRID_SIZE = 60; // size of grid
	this.TILE_WIDTH = 90;
	this.TILE_HEIGHT = 120;
  this.SIZE_PX = this.SIZE * this.GRID_SIZE;
	this.TILE_X_OFFSET = 15;
	this.TILE_Y_OFFSET = 60;
	this.TILES_PER_ROW = 8;
	this.TILES_PER_COLLUMN = 8;
	this.NUM_TILES = this.TILES_PER_ROW * this.TILES_PER_COLLUMN;
  
  this.SOLID_TILES_MIN_ID = 24;
  this.SOLID_TILES_MAX_ID = 55;
  this.DARK_TILE_ID = 64;
  
  // Items in dark 
  this.ITEM_IN_DARK = 0.5; 
  
  this.RAND_ITEM_DISTRIBUTION =  {food: 0.4, balloonPiece: 0.4, weapon: 0.2};
  
  this.RAND_FOOD_DISTRIBUTION = {apple: 0 , banana: 0, bread: 0.3, chicken: 0.3, rice: 0.4};
  
  this.RAND_PIECE_DISTRIBUTION = {hull: 0, rope: 0.50, bag: 0.50, lattern: 0, basket: 0};
  
  this.RAND_WEAPON_DISTRIBUTION = {knife: 0, machete: 0.50, axe: 0.45, chainsaw: 0.05 };
	
  
	this.tiles; // 2D array that holds id of the tiles
	
	this.waterTick = 0;
	
	
	this.init = function(level) {
    
    this.RAND_ITEM_DISTRIBUTION =  level.itemDistribution;
    this.RAND_FOOD_DISTRIBUTION = level.foodDistribution;  
    this.RAND_PIECE_DISTRIBUTION = level.ballonPieceDistribution; 
    this.RAND_WEAPON_DISTRIBUTION = level.waponDistribution;
    
	  this.tiles = mapGenerator.generateMap(level.generatorLevel);
	};
	

	
	this.draw  = function() {
	  
	  var autumnAlpha = 0.1 + (0.7 * (1 - (hero.water / hero.MAX_WATER)));
		
		var heroPosition = hero.getPosition();
		var heroTile = this.getTileCoordinates(heroPosition);
		var hitParticleTile = this.getTileCoordinates(hero.targetTileCoords);

		var viewPosition = this.findViewPosition(heroPosition);
    
		var visibleTiles = this.findVisibleTiles(viewPosition);	
		
		for(var y = 0; y < 4; y++) {
	    for(var x = 0; x < 5; x++) {
	      img.draw("grass", (x * 400) - (viewPosition.x % 400), (y * 400) - (viewPosition.y % 400));
	      c.globalAlpha = autumnAlpha;
	      img.draw("grassAutumn", (x * 400) - (viewPosition.x % 400), (y * 400) - (viewPosition.y % 400));
	      c.globalAlpha = 1;
	    }
		}
		
		for (var y = visibleTiles.yMin; y < visibleTiles.yMax; y++) {
      for (var x = visibleTiles.xMin;  x < visibleTiles.xMax; x++) {
        if(this.tiles[y][x] == 64) {
          var xPos = (x * this.GRID_SIZE) - viewPosition.x;
          var yPos = (y * this.GRID_SIZE) - viewPosition.y;
          img.draw("darkForest", xPos - 50, yPos - 50);
        }
      }
    } 
		
    var tileX = viewPosition.x + this.TILE_X_OFFSET;
    var tileY = viewPosition.y + this.TILE_Y_OFFSET;
    
		for (var y = visibleTiles.yMin; y < visibleTiles.yMax; y++) {
		  
      for (var x = visibleTiles.xMin;  x < visibleTiles.xMax; x++) {
        
        var xPos = (x * this.GRID_SIZE) - tileX;
        var yPos = (y * this.GRID_SIZE) - tileY;
        
        if((this.tiles[y][x] >= 5 && this.tiles[y][x] <= 7) || (this.tiles[y][x] >= 52 && this.tiles[y][x] <= 55)) {
          var tileCoordinates = this.idToSpriteCoordinates(this.tiles[y][x]);
          img.drawSprite("tileMap", xPos, yPos, this.TILE_WIDTH, this.TILE_HEIGHT, tileCoordinates.x, tileCoordinates.y);
          c.globalAlpha = autumnAlpha;
          img.drawSprite("tileMapAutumn", xPos, yPos, this.TILE_WIDTH, this.TILE_HEIGHT, tileCoordinates.x, tileCoordinates.y);
          c.globalAlpha = 1;
        } else if((this.tiles[y][x] >= 8 && this.tiles[y][x] <= 23) || (this.tiles[y][x] >= 48 && this.tiles[y][x] <= 51)) {
          var tileCoordinates = this.idToSpriteCoordinates(this.tiles[y][x]);
          img.drawSprite("tileMap", xPos, yPos, this.TILE_WIDTH, this.TILE_HEIGHT, tileCoordinates.x, tileCoordinates.y);
        } else if(this.tiles[y][x] >= 56 && this.tiles[y][x] <= 63) {
          var tileCoordinates = this.idToSpriteCoordinates(this.tiles[y][x]);
          img.drawSprite("tileMap", xPos, yPos, this.TILE_WIDTH, this.TILE_HEIGHT, tileCoordinates.x, tileCoordinates.y + Math.floor(this.waterTick / 5));
        } else if(this.tiles[y][x] == 67) {
          img.draw("yggdrasil", xPos, yPos - 60);
        }
      }
      
      if(heroTile.y == y) {
        hero.draw(viewPosition);
      }
			
			for (var x = visibleTiles.xMin;  x < visibleTiles.xMax; x++) {
				
			  var xPos = (x * this.GRID_SIZE) - tileX;
        var yPos = (y * this.GRID_SIZE) - tileY;
				
        if(this.tiles[y][x] >= 24 && this.tiles[y][x] <= 47) {
  				var tileCoordinates = this.idToSpriteCoordinates(this.tiles[y][x]);
  				img.drawSprite("tileMap", xPos, yPos, this.TILE_WIDTH, this.TILE_HEIGHT, tileCoordinates.x, tileCoordinates.y);
          c.globalAlpha = autumnAlpha;
          img.drawSprite("tileMapAutumn", xPos, yPos, this.TILE_WIDTH, this.TILE_HEIGHT, tileCoordinates.x, tileCoordinates.y);
          c.globalAlpha = 1;
        }
			}
			
      if(hitParticleTile.y == y) {
        hero.drawParticles(viewPosition);
      }
		}	
	};
	
	
	this.update = function() {
	  this.waterTick++;
	  if(this.waterTick >= 20) {
	    this.waterTick = 0;
	  }
	};
	
	
	this.findViewPosition = function(heroPosition) {
		
    var xOffset = game.width / 2; // Distance from hero to left border
    var yOffset = game.height / 2;
		
    // Avoid shifting among borders
    var viewPosition = {};
    viewPosition.x = heroPosition.x - xOffset;
    viewPosition.y = heroPosition.y - yOffset;

    viewPosition.x  =  setInRange(viewPosition.x, 0,this.SIZE_PX - game.width );
    viewPosition.y  =  setInRange(viewPosition.y, 0,this.SIZE_PX - game.height );
    
    viewPosition.x = Math.round(viewPosition.x);
    viewPosition.y = Math.round(viewPosition.y);
    
		return viewPosition;
	};
	
	// Returns a variable holding the mi
	this.findVisibleTiles = function(viewPosition) {
				
		var visibleTiles = {};
    
    var tileCoordinatesMin = this.getTileCoordinates(viewPosition);
    visibleTiles.xMin = tileCoordinatesMin.x;
    visibleTiles.yMin = tileCoordinatesMin.y;
    
    var lowerRightCorner = { x: viewPosition.x + game.width, y:viewPosition.y +  game.height };
    
    var tileCoordinatesMax = this.getTileCoordinates(lowerRightCorner);   
     
		visibleTiles.xMin = tileCoordinatesMin.x - 1;
		visibleTiles.xMax = tileCoordinatesMax.x + 1;
		visibleTiles.yMin = tileCoordinatesMin.y;
		visibleTiles.yMax = tileCoordinatesMax.y + 2;
		
    // Make sure that no tile is rendered, that doesen't exist
    visibleTiles.xMax = Math.min(visibleTiles.xMax, this.SIZE);
    visibleTiles.yMax = Math.min(visibleTiles.yMax, this.SIZE);
    
		return visibleTiles;
	};
  
  
  // Returns the tile coordinates for a point in world coordinates
  this.getTileCoordinates = function (point) {
    return { x : Math.floor(point.x / this.GRID_SIZE), y : Math.floor(point.y / this.GRID_SIZE) };
  };
  
  
  this.isTilePassable = function(tileCoordinates) {
    var id = this.getTileID(tileCoordinates);
    return (id < this.SOLID_TILES_MIN_ID || id > this.SOLID_TILES_MAX_ID);
  };
  	
  
	this.idToSpriteCoordinates = function(id) {
		return { x : (id % this.TILES_PER_ROW), y : Math.floor(id / this.TILES_PER_COLLUMN) };
	};
  
	
  this.spriteCoordinatesToId = function(sCoordinates) {
    var id = 0;
    id += sCoordinates.x * this.TILES_PER_COLLUMN;
    id += sCoordinates.y * this.TILES_PER_ROW;
    
    return id;
  };
  
  
  // To avoid confusion about tiles[y][x] and not tiles[x][y]
  this.getTileID = function(tileCoordinates) {
    return this.tiles[tileCoordinates.y][tileCoordinates.x];
  };
  
  
  // Returns the first tile with id tileID, (If there are more than one, the first one is returned.)
  this.getTileByID = function (tileID) {
    
		for (var y = 0; y < this.tiles.length; y++) {		
			for (var x = 0;  x < this.tiles[y].length; x++) {
			  if (this.tiles[y][x] == tileID) {
          var tileCoordinates = {};
          tileCoordinates.x = x;
          tileCoordinates.y = y;
          
			    return tileCoordinates;
			  }
			}
		}	
    
    return
  };

		
	
	//Draw debug version of map
	this.drawDebug = function() {
    var tile;
    for(var x = 0; x < this.SIZE; x++) {
      for(var y = 0; y < this.SIZE; y++) {
        tile = this.tiles[x][y];
        c.fillStyle = "#0f0";
        if(tile >= 0 && tile <= 7) {
          c.fillStyle = "#3c3";
        }
        if(tile >= 24 && tile <= 39) {
          c.fillStyle = "#060";
        }
        if(tile >= 40 && tile <= 47) {
          c.fillStyle = "#030";
        }
        if(tile >= 48 && tile <= 51) {
          c.fillStyle = "#666";
        }
        if(tile >= 52 && tile <= 55) {
          c.fillStyle = "#999";
        }
        if(tile >= 56 && tile <= 63) {
          c.fillStyle = "#33c";
        }
        if(tile >= 64 && tile <= 65) {
          c.fillStyle = "#010";
        }
        if(tile == 66) { // Start tile
          c.fillStyle = "#f00";
        }
        if(tile == 67) { // End tile
          c.fillStyle = "#00f";
        }
        c.fillRect(x * 8, y * 8, 8, 8);
      }
    }
	};
  
  this.tileCoordToWorldCoord = function(tileCoordinates) {
    var worldCoord = {};
      worldCoord.x = tileCoordinates.x * this.GRID_SIZE;
      worldCoord.y = tileCoordinates.y * this.GRID_SIZE;
      
      return worldCoord;
    
  };
  
  
  this.getSpawn = function() {
    for (var y = 0; y < this.tiles.length; y++) {   
      for (var x = 0;  x < this.tiles[y].length; x++) {
        if(this.tiles[y][x] == 66) {
          var tileCoordinates = {};
          tileCoordinates.x = (x + 0.5) * this.GRID_SIZE;
          tileCoordinates.y = (y + 0.5) * this.GRID_SIZE;
          return tileCoordinates;
        }
      }
    } 
  };
  
  
  // Call this method when a dark tree was cut down to check if dark wood should be lightened
  this.lightenTile = function (position) {
    
    var testOffsetX = [ -1, 0, 1, -1, 1, -1, 0, 1 ];
    var testOffsetY = [ -1, -1, -1, 0, 0, 1, 1, 1 ];
    
    for(var i = 0; i < 8; i++) {
      var testPosition = { x : position.x + testOffsetX[i], y : position.y + testOffsetY[i] };
      if (this.isValidTileCoordinate(testPosition) && this.getTileID(testPosition) == this.DARK_TILE_ID) {
        this.tiles[testPosition.y][testPosition.x] = this.getDarkTileReplacement();
        this.lightenTile(testPosition);
      }
    }
  };
  
  
  this.isValidTileCoordinate = function(position) {
    return (position.x >= 0 && position.x < this.GRID_SIZE && position.y >= 0 && position.y < this.GRID_SIZE);
  };
  
  
  this.getDarkTileReplacement = function() {
        
    if ( randFloat(0, 1) < this.ITEM_IN_DARK ) {
      
      var prob =[];
      for(var p in this.RAND_ITEM_DISTRIBUTION) { // TODO: That might fail 
        prob.push(this.RAND_ITEM_DISTRIBUTION[p]);
      }
      
      var funcList = [];
      var myMethod =  function(rThis){ return rThis.getRandomFood(rThis.RAND_FOOD_DISTRIBUTION);};
      funcList.push(myMethod); 
      myMethod = function(rThis){return rThis.getRandomBalloonPiece(rThis.RAND_PIECE_DISTRIBUTION);};
      funcList.push(myMethod); 
      myMethod = function(rThis){return rThis.getRandomWeapon(rThis.RAND_WEAPON_DISTRIBUTION);};
      funcList.push(myMethod); 
      
      var retFunc = getRandomItem(funcList, prob);
      return retFunc(this);
      
    }
    
    return 0;
  };
  
  // .apple .banana .bread .chicken .rice
  // var food = {apple: , banana: , bread: , chicken: , rice};
  this.getRandomFood = function(foodProb) {   
    var items = [8, 9, 10, 11, 12];
    var prob = [foodProb.apple, foodProb.banana, foodProb.bread, foodProb.chicken, foodProb.rice];
    
    return getRandomItem(items, prob);
  };  
  
  // var pieceProb = {hull: 0, rope: 0, bag: 0, lattern: 0, basket: 0}
  this.getRandomBalloonPiece = function(pieceProb) {
    var items = [16, 17, 18, 19, 20];
    var prob = [pieceProb.hull, pieceProb.rope, pieceProb.bag, pieceProb.lattern, pieceProb.basket];
    
    return getRandomItem(items, prob);
  };
  
  // var weaponProb = {knife: 0, machete: 0, axe: 0, chainsaw: 0 } TODO, beaver: 0}
  this.getRandomWeapon = function(weaponProb) {
    var items = [13, 14, 15, 21];
    var prob = [weaponProb.knife, weaponProb.machete, weaponProb.axe, weaponProb.chainsaw]; // TODO , weaponProb.beaver];
    
    return getRandomItem(items, prob);
  };
};